#include "platform\SectorManager.h"

#include <stdio.h>

#include "platform\base.h"
#include "platform\PlatFormMessage.h"
#include "raknet\RakNetTypes.h"
#include "raknet\MessageIdentifiers.h"
#include "raknet\RakPeer.h"
#include "raknet\RakPeerInterface.h"
#include "raknet\BitStream.h"
#include "raknet\Kbhit.h"

using namespace RakNet;

#define MAIN_SERVER_PORT 2001

SectorManager::SectorManager(): roomCount(0) 
{
	serverNode = RakPeerInterface::GetInstance();
	serverNode->SetIncomingPassword("", 0);
	serverNode->SetTimeoutTime(3000, UNASSIGNED_SYSTEM_ADDRESS);
	SocketDescriptor socketDescriptor;
	socketDescriptor.port = MAIN_SERVER_PORT;
	if (serverNode->Startup(100, &socketDescriptor, 1) == RAKNET_STARTED) 
	{
		serverNode->SetMaximumIncomingConnections(100);
		serverNode->SetOccasionalPing(true);
		serverNode->SetUnreliableTimeout(1000);
	} else 
	{
		serverNode->Shutdown(300);
		RakPeerInterface::DestroyInstance(serverNode);
	}
}

SectorManager::~SectorManager() {
	serverNode->Shutdown(300);
	RakNet::RakPeerInterface::DestroyInstance(serverNode);
}

void SectorManager::ProcessMessage() 
{
	while (true) {
		if (p = serverNode->Receive()) {
			BitStream ibstream(p->data, p->length, false);
			switch (p->data[0])
			{
			case ID_NEW_INCOMING_CONNECTION:
				cout << "player with address " << p->systemAddress.ToString() << " loged in.\n";
				break;
			case ID_CHECK:
				{
					BitStream bso;
					UpdateRoomInfo(&bso);
					SendMessage(&bso, p->systemAddress);
				}
				break;
			case ID_CREATE:
				{
					PlatformMessageCreate pmc;
					pmc.DeSerialization(&ibstream);
					BitStream bso; 
					CreateSector(pmc, p->systemAddress);
					bso.Write((uchar)ID_CREATE);
					cout << "create callback success.\n";
					SendMessage(&bso, p->systemAddress);
				}
				break;
			case ID_JOIN:
				{
					cout << "join.\n";
					PlatformMessageJoin pmj;
					pmj.DeSerialization(&ibstream);
					BitStream bso;
					bso.Write((uchar)ID_JOIN);
					if (JoinSector(pmj, p->systemAddress))	bso.Write((bool)true);
					else bso.Write((bool)false);
					SendMessage(&bso, p->systemAddress);
				}
				break;
			case ID_LEAVE:
				{
					cout << "leave.\n";
					PlatformMessageJoin pmj;
					pmj.DeSerialization(&ibstream);
					LeaveSector(pmj, p->systemAddress);
				}
				break;
			case ID_READY:
				{
					cout << "ready.\n";
					PlatformMessageJoin pmj;
					pmj.DeSerialization(&ibstream);
					BitStream bso;
					bso.Write((uchar)ID_READY);
					if (ReadySector(pmj, p->systemAddress))	bso.Write((bool)true);
					else bso.Write((bool)false);
					SendMessage(&bso, p->systemAddress);
				}
				break;
			case ID_READY_CANCEL:
				{
					cout << "ready cancel.\n";
					PlatformMessageJoin pmj;
					pmj.DeSerialization(&ibstream);
					BitStream bso;
					bso.Write((uchar)ID_READY_CANCEL);
					if (ReadySector(pmj, p->systemAddress))	bso.Write((bool)true);
					else bso.Write((bool)false);
					SendMessage(&bso, p->systemAddress);
				}
				break;
			case ID_GAME_START:
				break;
			case ID_CHECK_ROOM:
				{
					PlatformMessageJoin pmj;
					pmj.DeSerialization(&ibstream);
					BitStream bso;
					bso.Write((uchar)ID_CHECK_ROOM);
					GetRoomInfo(pmj, &bso);
					SendMessage(&bso, p->systemAddress);
				}
				break;
			case ID_CONNECTION_LOST:
				cout << "id_connection_lost.\n";
				break;
			case ID_DISCONNECTION_NOTIFICATION:
				cout << "id_diconnection_notification.\n";
				break;
			default:
				cout << "unhandled packet header " << (int)p->data[0] << endl;
			}
			serverNode->DeallocatePacket(p);
		}
		if (_kbhit())
		{
			char ch = _getch();
			switch (toupper(ch))
			{
			case 'L':
				ListRoom();
				break;
			}
		}
	}
}

void SectorManager::SendMessage(PlatformMessage* msg, const SystemAddress& addr) 
{
	BitStream bso;
	msg->Serialization(&bso);
	serverNode->Send(&bso, HIGH_PRIORITY, RELIABLE_ORDERED, 0, addr, false);
}

void SectorManager::SendMessage(BitStream* bso, const SystemAddress& addr) 
{
	serverNode->Send(bso, HIGH_PRIORITY, RELIABLE_ORDERED, 0, addr, false);
}

void SectorManager::BroadcastExcludePlayer(PlatformMessage* msg, const SystemAddress& addr)
{
	BitStream bso;
	msg->Serialization(&bso);
	serverNode->Send(&bso, HIGH_PRIORITY, RELIABLE_ORDERED, 0, addr, true);
}

void SectorManager::CreateSector(PlatformMessageCreate& msg, SystemAddress& sa)
{
	cout << msg.username << " create rooms.\n";
	sectorContainer.insert(
		pair<RakString, PlayerSector>(
			msg.username, 
			PlayerSector(msg.username, sa)
			)
		);
	++roomCount;
}

bool SectorManager::JoinSector(PlatformMessageJoin& pmj, SystemAddress& sa)
{
	map<RakString, PlayerSector>::iterator it= sectorContainer.begin();
	if ((it = sectorContainer.find(pmj.roomname)) == sectorContainer.end()) return false;
	if (!it->second.isPlaying && it->second.playerCount < 10)
	{
		it->second.addPlayer(pmj.username, sa);
		++it->second.playerCount;
		return true;
	}
	return false;
}

void SectorManager::LeaveSector(PlatformMessageJoin& pmj, SystemAddress& sa)
{
	map<RakString, PlayerSector>::iterator it= sectorContainer.begin();
	if ((it = sectorContainer.find(pmj.roomname)) == sectorContainer.end()) return;

	map<RakString, Player>playerList = it->second.getPlayerList();
	map<RakString, Player>::iterator it2 = playerList.begin();
	if ((it2 = playerList.find(pmj.username)) == playerList.end()) return;
	if (it2->second.sa != sa) return;
	playerList.erase(it2);
	--it->second.playerCount;

	if (it->second.sectorName == pmj.roomname) sectorContainer.erase(it);
	--roomCount;
#ifdef _DEBUG
	assert (roomCount >= 0);
#endif
}

bool SectorManager::ReadySector(PlatformMessageJoin& pmj, SystemAddress& sa)
{
	map<RakString, PlayerSector>::iterator it= sectorContainer.begin();
	if ((it = sectorContainer.find(pmj.roomname)) == sectorContainer.end()) return false;

	map<RakString, Player>playerList = it->second.getPlayerList();
	map<RakString, Player>::iterator it2 = playerList.begin();
	if ((it2 = playerList.find(pmj.username)) == playerList.end()) return false;
	if (it2->second.sa != sa) return false;
	it2->second.ready = true;
	return true;
}
bool SectorManager::CancelReadySector(PlatformMessageJoin& pmj, SystemAddress& sa)
{
	map<RakString, PlayerSector>::iterator it = sectorContainer.begin();
	if ((it = sectorContainer.find(pmj.roomname)) == sectorContainer.end()) return false;

	map<RakString, Player>playerList = it->second.getPlayerList();
	map<RakString, Player>::iterator it2 = playerList.begin();
	if ((it2 = playerList.find(pmj.username)) == playerList.end()) return false;
	if (it2->second.sa != sa) return false;
	it2->second.ready = false;
	return true;
}

void SectorManager::UpdateRoomInfo(BitStream* bso)
{
	bso->Write((uchar)ID_CHECK);
	int size = sectorContainer.size();
	bso->Write(size);
	for (map<RakString, PlayerSector>::iterator it = sectorContainer.begin();
		it != sectorContainer.end(); it++)
	{
		bso->Write(it->first);
		bso->Write(it->second.playerCount);
		bso->Write(it->second.isPlaying);
	}
}

void SectorManager::ListRoom()
{
	cout << "There are " << roomCount << " rooms in total.\n";
	for (map<RakString, PlayerSector>::iterator it = sectorContainer.begin();
		it != sectorContainer.end(); it++)
	{
		cout << "name: " << it->first << endl;
		cout << "playerCount: " << it->second.playerCount << endl;
		cout << endl;
	}
}

bool SectorManager::GetRoomInfo(PlatformMessageJoin& pmj, BitStream* bso)
{
	map<RakString, PlayerSector>::iterator it = sectorContainer.find(pmj.roomname);
	if (it == sectorContainer.end()) {
		bso->Write((bool)false);
		return false;
	}
	map<RakString, Player>::iterator it_p = it->second.getPlayerList().find(pmj.username);
	if (it_p == it->second.getPlayerList().end()) {
		bso->Write((bool)false);
		return false;
	}
	bso->Write((bool)true);
	it->second.Serialization(bso);
	return true;
}